/**
 * To manage all application configuration items (key/value):
 * 1. Users need to save the initial configuration items in the project's `src/config.json`. The configuration file should retain the key/value format (comments are supported). The value can only be a string or a number, for example:
 * {
 *      //MQTT related configuration
 *      "mqtt.ip":"192.168.2.3",
 *      "mqtt.port":6199,
 * }
 * 2. Custom configuration files are also supported. The path and identifier of a custom configuration file can be passed during initialization. This identifier must be passed for subsequent data reads and writes.
 * 3. When a user uses this component for the first time in the application, `init` needs to be initialized. Initialization will save the data in `config.json` to memory. Subsequent retrievals will be from memory.
 * 4. Users can read and write configurations from anywhere using `get` and `set`.
 * 5. If you need to save the modified configuration item's value to the configuration file (ensuring the new configuration takes effect after a restart), use `setAndSave`.
 * 6. If you need to restore all default settings, use `reset`.
 */
import dxMap from './dxMap.js'
import common from './dxCommon.js'
import std from './dxStd.js'

const map = dxMap.get("default")

const config = {}
const DEFALUT_OPTIONS = { path: '/app/code/src/config.json', savePath: '/app/data/config/config.json', flag: '___config.' }

/**
 * Initialization saves the data from config.json or a custom configuration file to memory, and subsequent retrievals are made from this memory.
 * @param {object} custom Optional, custom configuration file
 *          @param {string} custom.path Full path to the custom configuration file
 *          @param {string} custom.flag The identifier for the custom configuration file. Note that if there are multiple custom configuration files, this identifier should not be duplicated.
 */
config.init = function (custom) {
    if (custom) {
        if (!custom.path || !custom.flag) {
            throw new Error('The path and flag for the custom configuration file cannot be empty.')
        }
    }
    let flag = custom ? DEFALUT_OPTIONS.flag + custom.flag + '.' : DEFALUT_OPTIONS.flag;
    const isInited = map.get('___inited' + flag)
    if (isInited) {//Initialize only once
        return
    }
    let path = custom ? custom.path : DEFALUT_OPTIONS.path
    let savePath = custom ? '/app/data/config/config' + custom.flag + '.json' : DEFALUT_OPTIONS.savePath
    if (!std.exist(path)) {
        throw new Error('The config file not existed:' + path)
    }
    let existed = std.exist(savePath)
    let content = existed ? std.parseExtJSON(std.loadFile(savePath)) : std.parseExtJSON(std.loadFile(path))
    if (!existed) {
        std.saveFile(savePath, JSON.stringify(content))
    }
    for (let [key, value] of Object.entries(content)) {
        map.put(flag + key, value)
    }
    map.put('___inited' + flag, 'ok')
}
/**
 * Get all configuration items
 * @param {string} flag A custom configuration file identifier, which can be empty; if empty, it returns all the contents of the default config.json file.
 * @returns JSON object
 */
config.getAll = function (flag) {
    let _flag = _getFlag(flag)
    let configInfo = {}
    let keys = map.keys().filter(k => k.startsWith(_flag))
    keys.forEach(k => {
        let key = k.substring(_flag.length)
        let val = map.get(k)
        configInfo[key] = val
    })
    return configInfo
}
/**
 * Retrieve configuration, only retrieve from map.
 * If the configuration item is empty, return all data.
 * @param {string} key Configuration items 
 * @param {string} flag A custom configuration file identifier, which can be empty; if empty, it returns the default configuration value from config.json.
 * @returns 
 */
config.get = function (key, flag) {
    if (!key) {
        return this.getAll(flag);
    }
    let _flag = _getFlag(flag)
    return map.get(_flag + key)
}

/**
 * Update configuration, only modify map
 * @param {string} key Configuration items 
 * @param {string} value Configuration value
 * @param {string} flag A custom configuration file identifier, which can be empty; if empty, it will point to the default configuration value in config.json.
 */
config.set = function (key, value, flag) {
    if (!key || value == null || value == undefined) {
        throw new Error("key or value should not be empty")
    }
    let _flag = _getFlag(flag)
    map.put(_flag + key, value)
}

/**
 * Persist the data in the map to local storage.
 * @param {string} flag A custom configuration file identifier, which can be empty; if empty, it will point to the default configuration value in config.json.
 */
config.save = function (flag) {
    //save
    std.saveFile(_getSavePath(flag), JSON.stringify(this.getAll(flag)))
}

/**
 * Update configuration, modify map and persist locally
 * @param {string} key Configuration items 
 * @param {string} value Configuration value
 * @param {string} flag A custom configuration file identifier, which can be empty; if empty, it will point to the default configuration value in config.json.
 */
config.setAndSave = function (key, value, flag) {
    this.set(key, value, flag)
    //save
    std.saveFile(_getSavePath(flag), JSON.stringify(this.getAll(flag)))
}

/**
 * Reset. Please restart your device after resetting.
 * @param {string} flag A custom configuration file identifier, which can be empty; if empty, it will point to the default configuration value in config.json.
 */
config.reset = function (flag) {
    common.systemBrief('rm -rf ' + _getSavePath(flag))
}

//-------------------private-------------------------------

function _getFlag(flag) {
    return flag ? DEFALUT_OPTIONS.flag + flag + '.' : DEFALUT_OPTIONS.flag
}
function _getSavePath(flag) {
    return flag ? '/app/data/config/config' + flag + '.json' : DEFALUT_OPTIONS.savePath
}
export default config;